package aiproject3.predictors;

import java.util.LinkedList;

import neuralnet.NeuralNetwork;

/**
 * A predictor class for characters using an artificial neural network.
 * 
 * @author John Schaeffer
 *
 */
public class ANNPredictor implements Predictor<Character> {

    // The network in which the overall knowledge is stored.
    private NeuralNetwork data;
    
    // The queue of previous characters.
    private LinkedList<Character> prevSeq;
    
    // The input array for the network.
    private double inputs[];
    
    // The sequence size of the previous characters.
    private int seqSize = 6;
    
    // The current sequence length (only goes up to seqSize, used in the beginning).
    private int curSeqLen = 0;
    
    /**
     * Constructs a new ANNPredictor. 
     */
    public ANNPredictor() {
        data = new NeuralNetwork(seqSize,1,1,8,-1.0,0.2,0.1);
        prevSeq = new LinkedList<Character>();
        inputs = new double[seqSize];
    }
    
    /**
     * Predicts the next character based on the previous sequence of characters and
     * knowledge acquired so far.
     */
    public Character predictNext() {
        int i = 0;
        
        // Go through the entire previous sequence.
        for(Character c : prevSeq) {
            // Scale the inputs.
            inputs[i] = (double)(c - 'A') / 26;
            System.out.print(c + " ");
        }
        System.out.println();
        
        // Feed the data forward and unscale the outputs.
        double[] out = data.feedForward(inputs);
        char rtn = (char)((out[0] * 26 + 0.5) + 'A');

        return rtn;
    }

    /**
     * Update the predictor by training it with the previous sequence and current
     * character.
     * 
     * @param unit The character used to update the network.
     */
    public void updatePredictor(Character unit) {
        
        System.out.println("Updating network with " + unit);
        // If we still don't have a full queue, just add the unit and do nothing else.
        if(curSeqLen < seqSize) {
            prevSeq.add(unit);
            curSeqLen++;
        }
        
        // Scale inputs and iterate through the entire sequence queue.
        int i = 0;
        for(Character c : prevSeq) {
            // Scale the inputs.
            inputs[i] = (double)(c - 'A') / 26;
            i++;
        }
        
        // Scale desired output and train on that for some iterations.
        double[] outputArr = {(double)(unit - 'A') / 26};
        data.train(inputs, outputArr, 50);
        
        // If the queue is full, remove the oldest character and add a new one.
        if(curSeqLen >= seqSize) {
            prevSeq.remove();
            prevSeq.add(unit);
        }
    }

}
